package suites.logger {
	import org.flexunit.asserts.assertEquals;
	import org.flexunit.asserts.assertTrue;
	import org.flexunit.asserts.fail;
	import org.flexunit.async.Async;
	import org.splink.library.logging.ILogger;
	import org.splink.library.logging.ILoggerFactory;
	import org.splink.library.logging.LoggerFactory;
	import org.splink.library.logging.LoggerProvider;

	import flash.events.Event;
	import flash.utils.clearInterval;
	import flash.utils.setInterval;

	/**
	 * @author maxmc
	 */
	public class LogOutputInitTest {
		private static var _factoryA : ILoggerFactory;
		private static var _factoryB : ILoggerFactory;
		private static var _factoryC : ILoggerFactory;
		private static var _logsA : Array;
		private static var _logsB : Array;
		private static var _logsC : Array;
		private var _handleEventFnc : Function;

		final private function handleAsync(timeout : int) : Function {
			return _handleEventFnc = Async.asyncHandler(this, function() : void {
			}, timeout, null, handleTimeout);
		}

		private function handleTimeout(passThroughData : Object) : void {
			fail("Timeout reached before event");
		}

		private function complete() : void {
			_handleEventFnc(new Event(Event.COMPLETE));
		}

		[BeforeClass]
		public static function beforeClass() : void {
			_factoryA = new LoggerFactory("A");
			_factoryA.addLogOutput(new TestOutput(_logsA = new Array(), 20));
			LoggerProvider.addLoggerFactory(_factoryA);

			_factoryB = new LoggerFactory("B");
			_factoryB.addLogOutput(new TestOutput(_logsB = new Array(), 300));
			LoggerProvider.addLoggerFactory(_factoryB);

			_factoryC = new LoggerFactory("C");
			_factoryC.addLogOutput(new TestOutput(_logsC = new Array(), 500));
			LoggerProvider.addLoggerFactory(_factoryC);
		}

		[AfterClass]
		public static function afterClass() : void {
			LoggerProvider.removeLoggerFactory(_factoryA);
			LoggerProvider.removeLoggerFactory(_factoryB);
			LoggerProvider.removeLoggerFactory(_factoryC);
		}

		[Before]
		public function before() : void {
			_logsA.splice(0, _logsA.length);
			_logsB.splice(0, _logsB.length);
			_logsC.splice(0, _logsC.length);
		}

		private function getLoggerA() : ILogger {
			return LoggerProvider.getLogger("A", LogOutputInitTest);
		}

		private function getLoggerB() : ILogger {
			return LoggerProvider.getLogger("B", LogOutputInitTest);
		}

		private function getLoggerC() : ILogger {
			return LoggerProvider.getLogger("C", LogOutputInitTest);
		}
		
		/**
		 * Each <code>ILoggerFactory</code> maintains the log message order of it's <code>ILogger</code>s logs. 
		 * After all <code>ILogOutput</code>s of a logger factory are initialized, the accumulated logs are output 
		 * in the order they occurred. 
		 * IF there is 
		 * more then one factory active 
		 * and all factories output their logs to the same destination (for instance QLog)
		 * and the <code>ILogOutput</code> needs some initialization time (for instance <code>QLogOutput</code>)
		 * THEN the order in which the logs appear (for instance in QLog) might seem incorrect. 
		 * 
		 * But that's part of the system's nature, because the initialization time of the <code>ILogOutput</code>s 
		 * needs not always the same amounts of time. When the <code>ILogger</code> flushes all logs which have been
		 * stacked until all of the logger's outputs were initialized, the logmessages of loggers which originated
		 * from other <code>ILoggerFactory</code>s aren't considered and thus not flushed. These logs are not flushed
		 * until the outputs of their <code>ILoggerFactory</code> are intialized, even if the logs happened before the
		 * already flushed logs.
		 */
		[Test(async)]
		public function test() : void {
			handleAsync(2000);
			
			getLoggerB().warn("warn");
			getLoggerB().error("error");
			getLoggerC().fatal("fatal");
			getLoggerA().info("info");
			getLoggerA().debug("debug");
			getLoggerA().trace("trace");

			var id : int = setInterval(function() : void {
				assertEquals(3, _logsA.length);
				assertTrue(String(_logsA[0]).indexOf("INFO info") > -1);
				assertTrue(String(_logsA[1]).indexOf("DEBUG debug") > -1);
				assertTrue(String(_logsA[2]).indexOf("TRACE trace") > -1);
				
				assertEquals(2, _logsB.length);
				assertTrue(String(_logsB[0]).indexOf("WARN warn") > -1);
				assertTrue(String(_logsB[1]).indexOf("ERROR error") > -1);
				
				assertEquals(1, _logsC.length);
				assertTrue(String(_logsC[0]).indexOf("FATAL fatal") > -1);

				clearInterval(id);
				complete();
			}, 1000);
		}
	}
}
import org.splink.library.logging.LogEvent;
import org.splink.library.logging.logoutput.ILogOutput;
import org.splink.library.logging.logoutput.IOutputFormatter;

import flash.utils.clearInterval;
import flash.utils.setInterval;

internal class TestOutput implements ILogOutput {
	private var _formatter : IOutputFormatter;
	private var _logs : Array;
	private var _intervalId : uint;
	private var _initialized : Boolean = false;

	public function TestOutput(logs : Array, delay : uint) {
		_logs = logs;
		_intervalId = setInterval(function() : void {
			_initialized = true;
			clearInterval(_intervalId);
		}, delay);
	}

	public function setOuputFormatter(formatter : IOutputFormatter) : void {
		_formatter = formatter;
	}

	public function getInitialized() : Boolean {
		return _initialized;
	}

	public function log(event : LogEvent) : void {
		_logs.push(_formatter.format(event));
	}
}